home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / components / flockFavoritesService.js < prev    next >
Text File  |  2007-10-12  |  57KB  |  1,766 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16. //
  17.  
  18. // XPConnect Helpers
  19. var Cc = Components.classes;
  20. var Ci = Components.interfaces;
  21. var Cr = Components.results;
  22.  
  23.  
  24. // Constants 
  25. const FAVORITES_RDF_FILE           = "flock-data.rdf";
  26. const OLD_FAVORITES_RDF_FILE       = "flock_favorites.rdf";
  27. const OLD_FAVORITES_RDF_FILE_RELIC = "flock_favorites_old.rdf";
  28. const FAVORITES_CID                = Components.ID("{3606f63a-f874-42fa-8174-cc9a0dd0a712}");
  29.  
  30. // Contract IDs
  31. const FAVORITES_CONTRACTID     = "@flock.com/favorites-service;1";
  32. const CONTAINER_CONTRACTID     = "@mozilla.org/rdf/container;1";
  33. const DIRECTORY_SVC_CONTRACTID = "@mozilla.org/file/directory_service;1";
  34. const LOCAL_FILE_CONTRACTID    = "@mozilla.org/file/local;1";
  35.  
  36. // Service handles
  37. const RDFS = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
  38. const RDFCU = Cc["@mozilla.org/rdf/container-utils;1"].getService(Ci.nsIRDFContainerUtils);
  39. const PREFS = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
  40. const IOS = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  41.  
  42. // Namespaces Helpers
  43. function Namespace(ns) { return function (arg) { return RDFS.GetResource(ns+arg); } }
  44. const W3RDF  = Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#");
  45. const NSRDF  = Namespace("http://home.netscape.com/NC-rdf#");
  46. const WBRDF  = Namespace("http://home.netscape.com/WEB-rdf#");
  47. const FLRDF  = Namespace("http://flock.com/rdf#");
  48. const COLRDF = Namespace("urn:flock:collection:");
  49.  
  50. // RDF Properties
  51. const RDF_URL              = NSRDF("URL");
  52. const RDF_TAGS             = FLRDF("tags");
  53. const RDF_TAG              = FLRDF("tag");
  54. const RDF_WSID             = FLRDF("wsid");
  55. const RDF_NAME             = NSRDF("Name");
  56. const RDF_PERMISSIONS      = FLRDF("permissions");
  57. const RDF_TYPE             = W3RDF("type");
  58. const RDF_FAVORITE         = FLRDF("Favorite");
  59. const RDF_COLLECTION       = FLRDF("Collection");
  60. const RDF_FOLDER           = FLRDF("Folder");
  61. const RDF_LIVEMARK         = NSRDF("Livemark");
  62. const RDF_FEEDURL          = NSRDF("FeedURL");
  63. const RDF_FEEDENABLED      = FLRDF("feedEnabled");
  64. const RDF_SHORTCUTURL      = NSRDF("ShortcutURL");
  65. const RDF_DESCRIPTION      = NSRDF("Description");
  66. const RDF_NSYNC            = FLRDF("nsync");
  67. const RDF_LOCAL            = FLRDF("local");
  68. const RDF_LASTMODIFIEDDATE = WBRDF("LastModifiedDate");
  69. const RDF_LASTVISITDATE    = WBRDF("LastVisitDate");
  70. const RDF_ADDEDDATE        = FLRDF("FavoriteAddDate");
  71. const RDF_FAVORITESROOT    = FLRDF("BookmarksRoot");
  72. const RDF_COLLECTIONSROOT  = FLRDF("CollectionsRoot");
  73. const RDF_FOLDERSROOT      = FLRDF("FoldersRoot");
  74. const RDF_TAGSROOT         = FLRDF("TagsRoot");
  75. const RDF_INSTANCEOF       = W3RDF("instanceOf");
  76. const RDF_SEQ              = W3RDF("Seq");
  77. const RDF_FEED             = FLRDF("feed");
  78. const RDF_TOOLBAR          = FLRDF("toolbar");
  79. const RDF_STORE            = FLRDF("store");
  80. const RDF_VERSION          = FLRDF("version");
  81. const RDF_HASFEEDS         = FLRDF("hasFeeds");
  82. const RDF_ISFEED           = FLRDF("isFeed");
  83. const RDF_COOPTYPE         = FLRDF("CoopType");
  84.  
  85. // RDF Helpers
  86. var rdfLiteral = RDFS.GetLiteral;
  87. var rdfDate    = RDFS.GetDateLiteral;
  88. var rdfInt     = RDFS.GetIntLiteral;
  89. function rdfBoolean(b) { return rdfLiteral(b ? "true" : "false"); }
  90. const RDF_TRUE  = rdfBoolean(true);
  91. const RDF_FALSE = rdfBoolean(false);
  92.  
  93. // Prefs
  94. const PREF_INDEXED_INITIAL_FAVORITES = "flock.favorites.indexedInitialFavorites";
  95. const PREF_FIRSTRUN_COMPLETED        = "flock.firstrun.components.browser_startup.completed";
  96. const PREF_MYWORLD_TOPSITES_COUNT    = "flock.myworld.topsites.count";
  97. const PREF_MYWORLD_TOPSITES_COUNT_DEFAULT = 25;
  98.  
  99. const RECOUNT_INTERVAL = 500; // half-second delay in propagating count changes
  100.  
  101. // Cardinal migration
  102. const CARDINAL_BM_TOOLBAR_DEFAULT_NAME = "Favorites Toolbar";
  103.  
  104. function getObserverService() {
  105.   return Cc['@mozilla.org/observer-service;1']
  106.     .getService(Ci.nsIObserverService);
  107. }
  108.  
  109. // Remove Top Site
  110. function removeSite(aFolderURN, aResource, aCoopDS) {
  111.   var c_Folder = aCoopDS.get(aFolderURN);
  112.  
  113.   // Get Children of c_Folder
  114.   var childEnum = c_Folder.children.enumerate();
  115.  
  116.   // Get fav of aNewResource
  117.   aResource.QueryInterface(Ci.nsIRDFResource);
  118.   var fav = aCoopDS.get_from_resource(aResource);
  119.  
  120.   if(!fav) return;
  121.  
  122.   while (childEnum.hasMoreElements()) {
  123.     var child = childEnum.getNext();
  124.  
  125.     if(child.id() == fav.id()) {
  126.       // We found our fav
  127.       c_Folder.children.remove(child);
  128.     }
  129.   }
  130. }
  131. // Bubble Sorting Routine
  132. function bubbleSort(aFolderURN, aNewResource, aCompareFunction, aCoopDS) 
  133. {
  134.   var bInserted = false;
  135.  
  136.   var c_Folder = aCoopDS.get(aFolderURN);
  137.  
  138.   // Get Children of c_Folder
  139.   var childEnum = c_Folder.children.enumerate();
  140.  
  141.   // Get fav of aNewResource
  142.   aNewResource.QueryInterface(Components.interfaces.nsIRDFResource);
  143.   var newFav = aCoopDS.get_from_resource(aNewResource);
  144.  
  145.   var aTopSiteCount = 0;
  146.  
  147.   var bAlreadySeen = false;
  148.  
  149.   var iMaxCount = PREF_MYWORLD_TOPSITES_COUNT_DEFAULT;
  150.   if(PREFS.getPrefType(PREF_MYWORLD_TOPSITES_COUNT))
  151.   {
  152.     iMaxCount = PREFS.getIntPref(PREF_MYWORLD_TOPSITES_COUNT);
  153.   }
  154.   else
  155.   {
  156.     PREFS.setIntPref(PREF_MYWORLD_TOPSITES_COUNT, iMaxCount);
  157.   }
  158.  
  159.   while (childEnum.hasMoreElements()) {
  160.       
  161.     aTopSiteCount++;
  162.   
  163.     var child = childEnum.getNext();   
  164.  
  165.     // If we have NOT inserted, and this child is the same as what we want to insert
  166.     // BAIL out.
  167.     if(!bInserted && checkDupe(aFolderURN, newFav, child)) {
  168.       return;
  169.     }
  170.  
  171.     // If we have NOT inserted yet, check for an appropriate spot
  172.     if (!bInserted && (aCompareFunction(newFav,child) >= 0))
  173.     {
  174.       var aIndex = c_Folder.children.indexOf(child);
  175.  
  176.       while(c_Folder.children.has(newFav)) {
  177.         c_Folder.children.remove(newFav);
  178.       }
  179.  
  180.       c_Folder.children.insertAt(newFav,aIndex);
  181.  
  182.       bInserted = true;
  183.       break;
  184.     }
  185.   }
  186.  
  187.   // Never inserted
  188.   // no items were in the collection before OR it is the oldest item.  
  189.   // Insert only if there's room
  190.   if (!bInserted && (aTopSiteCount+1 <= iMaxCount)) {
  191.     c_Folder.children.insertAt(newFav,aTopSiteCount+1);
  192.   }
  193.  
  194.  
  195.   // Go through the list again and make sure we remove the max elements
  196.   var e = c_Folder.children.enumerate();
  197.  
  198.   var iIndex = 0;
  199.   while(e.hasMoreElements()) {
  200.     var maxchild = e.getNext();
  201.     iIndex++;
  202.  
  203.     // We're over our allotted amount.. remove!
  204.     if (iIndex > iMaxCount && maxchild) {
  205.       c_Folder.children.remove(maxchild);
  206.     } 
  207.   }
  208. }
  209.  
  210. function checkDupe(aFolderURN, newFav, child) {
  211.   switch(aFolderURN) {
  212.     case "urn:flock:topsites": {
  213.       if(newFav.URL == child.URL)
  214.         return true;
  215.     } break;
  216.     case "urn:flock:topfeeds": {
  217.       if(newFav.URL == child.URL)
  218.         return true;
  219.     } break;
  220.     case "urn:flock:topmedia": {
  221.       if(newFav.name == child.name
  222.           && newFav.service == child.service) {
  223.         return true;
  224.       }
  225.     }
  226.   }
  227.  
  228.   return false;
  229. }
  230.  
  231. function convertMonth(month) {
  232.   switch(month) {
  233.   case 'Jan': {
  234.               return 1;
  235.             }break;
  236.   case 'Feb': {
  237.               return 2;
  238.             }break;
  239.   case 'Mar': {
  240.               return 3;
  241.             }break;
  242.   case 'Apr': {
  243.               return 4;
  244.             }break;
  245.   case 'May': {
  246.               return 5;
  247.             }break;
  248.   case 'Jun': {
  249.               return 6;
  250.             }break;
  251.   case 'Jul': {
  252.               return 7;
  253.             }break;
  254.   case 'Aug': {
  255.               return 8;
  256.             }break;
  257.   case 'Sep': {
  258.               return 9;
  259.             }break;
  260.   case 'Oct': {
  261.               return 10;
  262.             }break;
  263.   case 'Nov': {
  264.               return 11;
  265.             }break;
  266.   case 'Dec': {
  267.               return 12;
  268.             }break;
  269.   }
  270.  
  271.   return 1;
  272. }
  273.  
  274.  
  275. // This data structure contains all the information we need to determine and
  276. // maintain the list of "top" faves in each of the supported categories (Sites,
  277. // Feeds, Media).  The associative array keys are URNs of the folder nodes.
  278. //  'criteria' - array of predicate-object pairs used to identify the type
  279. //  'triggers' - array of type-source-predicate-object tuples (arc property
  280. //               operations) that will trigger the observer
  281. //  'observer' - callback for triggers, refreshes folder contents
  282. //  'compare'  - function used to sort items of that type
  283. var gTopFaves = [
  284.   {
  285.     urn: "urn:flock:topsites",
  286.     criteria: [[FLRDF("flockType"), rdfLiteral("bookmark")]],
  287.     triggers: [[Ci.flockIRDFObserver.WATCH_TYPES, null, WBRDF("LastModifiedDate"), null],
  288.                [Ci.flockIRDFObserver.TYPE_ALL, null, WBRDF("LastVisitDate"), null]
  289.               ],
  290.     observer: {
  291.       rdfChanged: function (aDS, aType, aSource, aPredicate, aTarget, aOldTarget) {
  292.         
  293.         if (!aSource) return;
  294.  
  295.         aSource.QueryInterface(Ci.nsIRDFResource);
  296.  
  297.         if(aType == Ci.flockIRDFObserver.TYPE_UNASSERT) {
  298.           // Remove this from topsites
  299.           removeSite(this.ft.urn, aSource, this.svc.faves_coop);
  300.         } else {
  301.           bubbleSort(this.ft.urn, aSource, this.ft.compare, this.svc.faves_coop);
  302.         }
  303.         
  304.         var obs = getObserverService();
  305.         obs.notifyObservers(null, 'refresh-myworld-topsites', false);
  306.       }
  307.     },
  308.     compare: function (aResourceA, aResourceB) {
  309.       // Compare the two resource's timestamp
  310.       // -1 = A older than B
  311.       // 0  = equivalent
  312.       // 1  = A newer than B
  313.       return aResourceA.LastVisitDate - aResourceB.LastVisitDate;
  314.     },
  315.     initialize: function (ft,svc,coopObj)
  316.     {
  317.       if (coopObj == null) {
  318.         coopObj = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton).getSingleton('chrome://browser/content/flock/common/load-faves-coop.js').wrappedJSObject.bookmarks_root;
  319.       }
  320.       
  321.       var e = coopObj.children.enumerate();
  322.  
  323.       while (e.hasMoreElements()){
  324.         var entry = e.getNext();
  325.  
  326.         if (entry.flockType == "bookmark") {
  327.           var favResource = RDFS.GetResource(entry.id());
  328.           bubbleSort(ft.urn, favResource, ft.compare, svc.faves_coop);
  329.         } else if (entry.flockType == "folder") {
  330.           this.initialize(ft,svc,entry);
  331.         }
  332.       }
  333.     }
  334.   },
  335.   {
  336.     urn: "urn:flock:topfeeds",
  337.     criteria: [[FLRDF("flockType"), rdfLiteral("feed")]],
  338.     triggers: [[Ci.flockIRDFObserver.TYPE_ALL, null, FLRDF("datevalue"), null],
  339.                [Ci.flockIRDFObserver.TYPE_ALL, null, FLRDF("unseenItems"), null],
  340.                [Ci.flockIRDFObserver.TYPE_ALL, null, FLRDF("isPollable"), null]
  341.               ],
  342.     observer: {
  343.       rdfChanged: function (aDS, aType, aSource, aPredicate, aTarget, aOldTarget) {
  344.         
  345.         if (!aSource) return;
  346.         aSource.QueryInterface(Ci.nsIRDFResource);
  347.  
  348.         var feedObj = this.svc.faves_coop.get_from_resource(aSource);
  349.  
  350.         if ((aType == Ci.flockIRDFObserver.TYPE_UNASSERT) ||
  351.             (feedObj && feedObj.isPollable == false)) {
  352.           removeSite(this.ft.urn, aSource, this.svc.faves_coop);
  353.           return; 
  354.         }
  355.  
  356.         var sync = {  // Check if it's a feed
  357.             notify: function(timer) {
  358.               if (!checkCriteria(this.ft.criteria,aSource,this.svc.dataSource)) {
  359.                 return; 
  360.               }
  361.  
  362.               // Check parent for feed subscription
  363.               var c_Feed = this.svc.faves_coop.get_from_resource(aSource);
  364.               var parents = c_Feed.getParents();
  365.               for each (var parent in parents) {
  366.                 var node = parent;
  367.                 while (!node.isInstanceOf(this.svc.faves_coop.FeedContext)) {
  368.                   var nodeParents = node.getParents();
  369.                   if (nodeParents.length)
  370.                     node = nodeParents[0];
  371.                   else
  372.                     break;
  373.                 }
  374.                 if (node.id() == "urn:flock:feedcontext:news") {
  375.                   bubbleSort(this.ft.urn, aSource, this.ft.compare, this.svc.faves_coop);
  376.                   break;
  377.                 }
  378.               }
  379.             }
  380.           }
  381.  
  382.         sync.ft = this.ft;
  383.         sync.svc = this.svc;
  384.         try {
  385.           timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  386.           timer.initWithCallback(sync, 5 * 1 * 50, Ci.nsITimer.TYPE_ONE_SHOT);
  387.         } catch (e) {
  388.           dump("Exception occurred in topfeeds: " + e + "\n");
  389.         }
  390.       }
  391.     },
  392.     compare: function (aResourceA, aResourceB) {
  393.       // A == newFav
  394.       return aResourceA.datevalue - aResourceB.datevalue;
  395.     },
  396.     initialize: function (ft,svc,coopObj)
  397.     {
  398.       if(coopObj == null) {
  399.         coopObj = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton).getSingleton('chrome://browser/content/flock/common/load-faves-coop.js').wrappedJSObject.get("urn:flock:feedcontext:news");
  400.       }
  401.  
  402.       // If we are still null, forget it
  403.       if(coopObj == null)
  404.         return;
  405.  
  406.       var e = coopObj.children.enumerate();
  407.  
  408.       while (e.hasMoreElements()){
  409.         var entry = e.getNext();
  410.  
  411.         var favResource = RDFS.GetResource(entry.id());
  412.  
  413.         if(entry.flockType == "feed"){
  414.           bubbleSort(ft.urn, favResource, ft.compare, svc.faves_coop);
  415.         } else if (entry.flockType == "folder") {
  416.           this.initialize(ft,svc,entry);
  417.         }
  418.       }
  419.     }
  420.   },
  421.   {
  422.     urn: "urn:flock:topmedia",
  423.     criteria: [[W3RDF("type"), NSRDF("MediaQuery")]],
  424.     triggers: [[Ci.flockIRDFObserver.TYPE_ALL, null, FLRDF("latestDate"), null]],
  425.     observer: {
  426.       rdfChanged: function (aDS, aType, aSource, aPredicate, aTarget, aOldTarget) {
  427.         
  428.         if (!aSource) return;
  429.         aSource.QueryInterface(Ci.nsIRDFResource);
  430.  
  431.         if(aType == Ci.flockIRDFObserver.TYPE_UNASSERT) {
  432.           // Don't process
  433.           return; 
  434.         }
  435.  
  436.         var sync = {  // Check if it's a media obj
  437.             notify: function(timer) {
  438.         
  439.               if (!checkCriteria(this.ft.criteria,aSource,this.svc.dataSource)) {
  440.                 return; 
  441.               }
  442.  
  443.               // Check parent for media subscription
  444.               var c_Media = this.svc.faves_coop.get_from_resource(aSource);
  445.               var parents = c_Media.getParents();
  446.               for (var i in parents) {
  447.                 var parent = parents[i];
  448.                 if (parent.id() == "urn:media:favorites") {
  449.                   bubbleSort(this.ft.urn, aSource, this.ft.compare, this.svc.faves_coop);
  450.                   break;
  451.                 }
  452.               }
  453.             }
  454.           }
  455.  
  456.         sync.ft = this.ft;
  457.         sync.svc = this.svc;
  458.  
  459.         timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  460.         timer.initWithCallback(sync, 5 * 1 * 50, Ci.nsITimer.TYPE_ONE_SHOT);
  461.       }
  462.     },
  463.     compare: function (aResourceA, aResourceB) {
  464.       // A == newFav
  465.       return aResourceA.latestDate - aResourceB.latestDate;
  466.     },
  467.     initialize: function (ft,svc,coopObj)
  468.     {
  469.       if(coopObj == null) {
  470.         coopObj = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton).getSingleton('chrome://browser/content/flock/common/load-faves-coop.js').wrappedJSObject.get("urn:media:favorites");
  471.       }
  472.  
  473.       // If we are still null, forget it
  474.       if(coopObj == null)
  475.         return;
  476.  
  477.       var e = coopObj.children.enumerate();
  478.  
  479.       while (e.hasMoreElements()){
  480.         var entry = e.getNext();
  481.       
  482.         var favResource = RDFS.GetResource(entry.id());
  483.         // JMC - MediaQuery is no longer CountedObject
  484.         if(entry.flockType == "media"){
  485.           bubbleSort(ft.urn, favResource, ft.compare, svc.faves_coop);
  486.         } else if (entry.flockType == "folder") {
  487.           this.initialize(ft,svc,entry);
  488.         }
  489.       }
  490.     }
  491.   }
  492. ];
  493.  
  494. // checks incoming criteria for a match
  495. function checkCriteria(aCriteriaArr,aSource,aDataSource) 
  496. {
  497.   aSource.QueryInterface(Components.interfaces.nsIRDFResource);
  498.  
  499.  
  500.   for (var i=0;i<aCriteriaArr.length;i++) {
  501.     var prop = aCriteriaArr[i][0].QueryInterface(Ci.nsIRDFResource); 
  502.     var value = aCriteriaArr[i][1].QueryInterface(Ci.nsIRDFNode); 
  503.  
  504.     if(!aDataSource.HasAssertion(aSource, prop, value, true)) {
  505.       return false;
  506.     }
  507.   }
  508.  
  509.   return true;
  510. }
  511.  
  512. // Component Utils (just used for the timer/scheduler)
  513. var gCompTK;
  514. function getCompTK() {
  515.   if (!gCompTK) {
  516.     var gCompTK = Cc["@flock.com/singleton;1"]
  517.       .getService(Ci.flockISingleton)
  518.       .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
  519.       .wrappedJSObject;
  520.   }
  521.   return gCompTK;
  522. }
  523.  
  524. function loadSubScript(spec)
  525. {
  526.   var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
  527.     .getService(Ci.mozIJSSubScriptLoader);
  528.   var context = {};
  529.   loader.loadSubScript(spec, context);
  530.   return context;
  531. }
  532.  
  533.  
  534. // nsISimpleEnumerator implementation 
  535. function simpleEnumerator(aArray)
  536. {
  537.   aArray.hasMoreElements = function () { 
  538.     return this.length != 0; 
  539.   }
  540.   aArray.getNext = function () { 
  541.     return this.shift(); 
  542.   }
  543.   return aArray;
  544. }
  545.  
  546.  
  547. function makeNSIArray(aArray) {
  548.   aArray.queryElementAt = function (aIndex, aIIDRef) {
  549.     return aArray[aIndex].QueryInterface(aIIDRef);
  550.   }
  551.   aArray.enumerate = function () { return simpleEnumerator(this); }
  552.   aArray._indexOf = aArray.indexOf;
  553.   aArray.indexOf = function (aStartIndex, aElement) { return this._indexOf(aElement, aStartIndex); }
  554.   return aArray;
  555. }
  556.  
  557.  
  558. function getProfDirFile()
  559. {
  560.   var dirService = Cc[DIRECTORY_SVC_CONTRACTID].getService(Ci.nsIProperties);
  561.   
  562.  
  563.   // create a datasource in the profile directory
  564.   var profd = dirService.get("ProfD", Ci.nsIFile);
  565.   var file = Cc[LOCAL_FILE_CONTRACTID].createInstance(Ci.nsILocalFile);
  566.   file.initWithPath(profd.path);
  567.   return file;
  568. }
  569.  
  570.  
  571. function base64Encode(aInput) {
  572.   var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  573.   var output = "";
  574.   while (aInput.length > 0) {
  575.     output += chars[aInput.charCodeAt(0) >> 2];
  576.     output += chars[((aInput.charCodeAt(0) & 0x03) << 4) | 
  577.       (aInput.length > 1 ? ((aInput.charCodeAt(1) & 0xF0) >> 4) : 0)];
  578.     output += chars[aInput.length > 1 ?
  579.       ((aInput.charCodeAt(1) & 0x0F) << 2) | 
  580.       (aInput.length > 2 ? ((aInput.charCodeAt(2) & 0xC0) >> 6) : 0) : 64];
  581.     output += chars[aInput.length > 2 ?
  582.       (aInput.charCodeAt(2) & 0x3F) : 64];
  583.     if (aInput.length > 3) {
  584.       aInput = aInput.substr(3);
  585.     } else {
  586.       break;
  587.     }
  588.   }
  589.   return output;
  590. }
  591.  
  592.  
  593.  
  594. // ======================================================
  595. // ========== BEGIN favoritesService Component ==========
  596. // ======================================================
  597.  
  598.  
  599. // BEGIN Constructor
  600. function favoritesService()
  601. {
  602.   this._logger = Cc["@flock.com/logger;1"].createInstance(Ci.flockILogger);
  603.   this._logger.init("favoritesService");
  604.   this._logger.info("Created Favorites Service Object");
  605.  
  606.   // JMC - Delay init until the DIRService Exists
  607.   this.loaded = false;
  608. }
  609. // END Constructor
  610.  
  611.  
  612. // BEGIN nsISupports
  613. favoritesService.prototype.QueryInterface =
  614. function (aIID)
  615. {
  616.   if ( !aIID.equals(Ci.nsIBookmarksService) && 
  617.        !aIID.equals(Ci.flockIFavoritesService) &&
  618.        !aIID.equals(Ci.flockIMigratable) &&
  619.        !aIID.equals(Ci.nsICharsetResolver) &&
  620.        !aIID.equals(Ci.nsIClassInfo) &&
  621.        !aIID.equals(Ci.nsIObserver) &&
  622.        !aIID.equals(Ci.nsISupports) )
  623.   {
  624.     throw Cr.NS_ERROR_NO_INTERFACE;
  625.   }
  626.   return this;
  627. }
  628. // END nsISupports
  629.  
  630.  
  631. // BEGIN nsIClassInfo
  632. favoritesService.prototype.flags = Ci.nsIClassInfo.SINGLETON;
  633. favoritesService.prototype.classDescription = "Flock Favorites Service";
  634. favoritesService.prototype.getInterfaces = function (count) {
  635.   var interfaceList = [ Ci.nsIBookmarksService, Ci.flockIFavoritesService,
  636.                         Ci.nsICharsetResolver,
  637.                         Ci.flockIMigratable, Ci.nsIClassInfo ];
  638.   count.value = interfaceList.length;
  639.   return interfaceList;
  640. }
  641. favoritesService.prototype.getHelperForLanguage = function (count) { return null; }
  642. // END nsIClassInfo
  643.  
  644.  
  645. // BEGIN nsIObserver
  646. favoritesService.prototype.observe = function (subject, topic, data)
  647. {
  648.   if ( topic == "nsPref:changed" &&
  649.        data == PREF_FIRSTRUN_COMPLETED &&
  650.        PREFS.getBoolPref(PREF_FIRSTRUN_COMPLETED) )
  651.   {
  652.     for (var folderURN in gTopFaves) {
  653.       var faveType = gTopFaves[folderURN];
  654.  
  655.       // start gTopFaves init function
  656.       faveType.initialize(faveType,this,null);
  657.     }
  658.  
  659.     if (!PREFS.getBoolPref(PREF_INDEXED_INITIAL_FAVORITES)) {
  660.       var index_initial_callback = {};
  661.       index_initial_callback.svc = this;
  662.       index_initial_callback.notify = function (timer) {
  663.         var e = this.svc.getAllFavorites();
  664.         while (e.hasMoreElements()) {
  665.           var u = e.getNext().QueryInterface(Ci.nsIRDFResource).ValueUTF8;
  666.           //this.svc.luceneIndexer.AddDocument(u);
  667.         }
  668.         PREFS.setBoolPref(PREF_INDEXED_INITIAL_FAVORITES, true);
  669.       }
  670.     }
  671.   }
  672.  
  673.   if (topic == "xpcom-shutdown") {
  674.     this.obs.removeObserver(this, "xpcom-shutdown");
  675.     try {
  676.       this.deleteTransients();
  677.     } catch (ex) {
  678.       this._logger.debug("ERROR deleting transient data!");
  679.       this._logger.debug(ex);
  680.     }
  681.   }
  682. }
  683. // END nsIObserver
  684.  
  685.  
  686. // BEGIN flockIFavoritesService
  687. favoritesService.prototype.setupDefaultFavorites = function ()
  688.   this._logger.info("SetupDefaultFavorites");
  689.   var faves_coop = this.faves_coop;
  690.   var favSrv = this;
  691.   var context = {coop: faves_coop, service: favSrv, dump: dump};
  692.   var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
  693.     .getService(Ci.mozIJSSubScriptLoader);
  694.   loader.loadSubScript("chrome://flock/locale/favorites/defaultFavorites.js", context);
  695.   
  696.   // Load up the defaul media favs too.
  697.   loader.loadSubScript("chrome://flock/locale/photo/defaultMedia.js", context);
  698. }
  699.  
  700. favoritesService.prototype.init = 
  701. function favoritesService_init(aDataSource)
  702. {
  703.   if (this.loaded) return;
  704.  
  705.   this.dataSource = aDataSource;
  706.   aDataSource.QueryInterface(Ci.flockIRDFObservable);
  707.   this.faves_coop = Cc["@flock.com/singleton;1"]
  708.     .getService(Ci.flockISingleton)
  709.     .getSingleton("chrome://browser/content/flock/common/load-faves-coop.js")
  710.     .wrappedJSObject;
  711.  
  712.   var prefsService = Cc["@mozilla.org/preferences-service;1"]
  713.                      .getService(Ci.nsIPrefService);
  714.   var favesBranch = prefsService.getBranch("flock.favorites.");
  715.  
  716.   // Convert 0.9.0.x LastVisitDate/LastModifiedDate from strings to real dates
  717.   var migratedStringDates;
  718.   try {
  719.     migratedStringDates = favesBranch.getBoolPref("migratedStringDates");
  720.   }
  721.   catch (ex) {
  722.     migratedStringDates = false;
  723.   }
  724.  
  725.   if (!migratedStringDates) {
  726.     var inst = this;
  727.  
  728.     function convertOldDate(aResource, aProp) {
  729.       var target = inst.dataSource.GetTarget(aResource, aProp, true);
  730.       if (target && target instanceof Ci.nsIRDFLiteral) {
  731.         var dateStr = target.QueryInterface(Ci.nsIRDFLiteral).Value;
  732.         var time = Date.parse(dateStr);
  733.         if (!isNaN(time)) {
  734.           inst.dataSource.Change(aResource, aProp, target,
  735.                                  RDFS.GetDateLiteral(1000 * time));
  736.         } else {
  737.           inst.dataSource.Unassert(aResource, aProp, target);
  738.         }
  739.       }
  740.     }
  741.  
  742.     var resources = this.faves_coop.Bookmark.all_ids();
  743.     while (resources.hasMoreElements()) {
  744.       var resource = resources.getNext();
  745.       convertOldDate(resource, RDF_LASTVISITDATE);
  746.       convertOldDate(resource, RDF_LASTMODIFIEDDATE);
  747.     }
  748.  
  749.     favesBranch.setBoolPref("migratedStringDates", true);
  750.   }
  751.  
  752.   // Make sure no favorite has more than one parent (only once)
  753.   var uniqueId = false;
  754.   try {
  755.     uniqueId = favesBranch.getBoolPref("uniqueid");
  756.   } catch(e) { /* The pref is not set yet */ }
  757.   if (!uniqueId) {
  758.     for (var i in gTopFaves) {
  759.       // Change the top favorites from the Folder type to TopFavesFolder
  760.       try {
  761.         var res = RDFS.GetResource(gTopFaves[i].urn);
  762.         var oldtype = this.dataSource.GetTarget(res, W3RDF("type"), true);
  763.         var oldcooptype = this.dataSource.GetTarget(res, FLRDF("CoopType"), true);
  764.         if (oldtype)
  765.           this.dataSource.Change(res, W3RDF("type"), oldtype, FLRDF("TopFavesFolder"));
  766.         if (oldcooptype)
  767.           this.dataSource.Change(res, FLRDF("CoopType"), oldcooptype, FLRDF("TopFavesFolder"));
  768.       }
  769.       catch (e) {}
  770.     }
  771.   
  772.     var allFaves = this.faves_coop.Bookmark.all();
  773.     var nonUnique = [];
  774.     while (allFaves.hasMoreElements()) {
  775.       var fave = allFaves.getNext();
  776.       // Assuming all bookmarks with an id starting with "urn:" are online
  777.       // faves, and thus should not be transformed. 
  778.       var urn = fave.id();
  779.       if (urn.substr(0, 4) == "urn:") {
  780.         continue;
  781.       }
  782.       var parentCount = 0;
  783.       for each (var parent in fave.getParents()) {
  784.         if (parent instanceof this.faves_coop.Folder)
  785.           parentCount++;
  786.       }
  787.       if (parentCount > 1) {
  788.         nonUnique.push(fave);
  789.       }
  790.     }
  791.  
  792.     for each (var fave in nonUnique) {
  793.       // This favorite has more than one parent.
  794.       // We need to clone it so each favorite has only one parent.
  795.       var firstparent = true;
  796.       var faveRes = RDFS.GetResource(fave.id());
  797.       for each (var parent in fave.getParents()) {
  798.         if (parent instanceof this.faves_coop.Folder) {
  799.           if (firstparent) {
  800.             // The first parent keeps the original favorite
  801.             firstparent = false;
  802.           }
  803.           else {
  804.             // Other parents get a clone
  805.             var newRes = this.cloneResource(faveRes);
  806.             var newFave = this.faves_coop.get(newRes.Value);
  807.  
  808.             var position = parent.children.indexOf(fave);
  809.             parent.children.insertAt(newFave, position);
  810.             parent.children.remove(fave);
  811.           }
  812.         }
  813.         else {
  814.           // MyWorld's "Top Faves" keeps the original
  815.         }
  816.       }
  817.     }
  818.     favesBranch.setBoolPref ("uniqueid", true);
  819.   }
  820.  
  821.   for (var folderURN in gTopFaves) {
  822.     // Make sure the sorting folders exist for each type
  823.     var faveType = gTopFaves[folderURN];
  824.     faveType.observer.ft = faveType;
  825.     faveType.observer.svc = this;
  826.     faveType.root = this.faves_coop.get(faveType.urn);
  827.     if (!faveType.root) {
  828.       faveType.root = new this.faves_coop.Folder(faveType.urn);
  829.     }
  830.     // Start watching for the appropriate arc changes
  831.     for (var t in faveType.triggers) {
  832.       var trig = faveType.triggers[t];
  833.       this.dataSource.addArcObserver(trig[0], trig[1], trig[2], trig[3],
  834.                                      faveType.observer);
  835.     }
  836.   }
  837.   
  838.   this._obsObsrv = {};
  839.   this._obsObsrv.observe = function main_observer(aSubject, aTopic, aVerb) {
  840.     
  841.     if( aTopic == "myworld-enableDisable-observers" ) {
  842.       if( aVerb == "true" ) {
  843.         // Re-enable the arc observers
  844.         for (var folderURN in this.gtf) {
  845.           // Make sure the sorting folders exist for each type
  846.           var faveType = this.gtf[folderURN];
  847.           faveType.observer.ft = faveType;
  848.           faveType.observer.svc = this.svc;
  849.           faveType.root = this.fc.get(faveType.urn);
  850.           if (!faveType.root) { faveType.root = new this.fc.TopFavesFolder(faveType.urn); }
  851.           // Start watching for the appropriate arc changes
  852.           for (var t in faveType.triggers) {
  853.             var trig = faveType.triggers[t];
  854.             this.ds.addArcObserver(trig[0], trig[1], trig[2], trig[3], faveType.observer);
  855.           }
  856.         }
  857.       } else {
  858.         // remove the arc observers
  859.         for (var folderURN in this.gtf) {
  860.           // Make sure the sorting folders exist for each type
  861.           var faveType = this.gtf[folderURN];
  862.           faveType.observer.ft = faveType;
  863.           faveType.observer.svc = this.svc;
  864.           faveType.root = this.fc.get(faveType.urn);
  865.           if (!faveType.root) { faveType.root = new this.fc.TopFavesFolder(faveType.urn); }
  866.           // Start watching for the appropriate arc changes
  867.           for (var t in faveType.triggers) {
  868.             var trig = faveType.triggers[t];
  869.             this.ds.removeArcObserver(trig[0], trig[1], trig[2], trig[3], faveType.observer);
  870.           }
  871.         }
  872.       } 
  873.     }
  874.   }
  875.   this._obsObsrv.fc   = this.faves_coop;
  876.   this._obsObsrv.svc  = this;
  877.   this._obsObsrv.ds   = this.dataSource;
  878.   this._obsObsrv.gtf  = gTopFaves;
  879.     
  880.   this.obs = Cc["@mozilla.org/observer-service;1"]
  881.     .getService(Ci.nsIObserverService);
  882.     
  883.   this.obs.addObserver(this._obsObsrv, "myworld-enableDisable-observers", false);
  884.   this.obs.addObserver(this, "xpcom-shutdown", false);
  885.  
  886.   // set up a prefs observer for the webservice id
  887.   var prefBranch2 = PREFS.QueryInterface(Ci.nsIPrefBranch2);
  888.   prefBranch2.addObserver(PREF_FIRSTRUN_COMPLETED, this, false); // FIXME: removeObserver sometime?
  889.  
  890.   this.deleteTransients();
  891.   this.loaded = true;
  892.   this._logger.info("flock favorites data source set up");    
  893. }
  894.  
  895. favoritesService.prototype.createBookmarkWithTags =
  896. function (aName, aURL, aShortcutURL, aDescription, aDocCharSet, aPostData, aTags)
  897. {
  898.   var bookmark = this.createNewBookmarkWithTags(aName, aURL, aShortcutURL, aDescription,
  899.                                                 aDocCharSet, aPostData, aTags);
  900.   this.faves_coop.bookmarks_root.children.addOnce(bookmark);
  901.   return RDFS.GetResource(bookmark.id())
  902. }
  903.  
  904. favoritesService.prototype.addBookmarkToFolder =
  905. function (aFolderName, aName, aURL, aShortcutURL, aDescription, aDocCharSet, aPostData, aTags)
  906. {
  907.   var folder = this.faves_coop.get(aFolderName);
  908.   var bookmark = this.createNewBookmarkWithTags(aName, aURL, aShortcutURL, aDescription,
  909.                                                 aDocCharSet, aPostData, aTags);
  910.   folder.children.addOnce(bookmark);
  911.   return RDFS.GetResource(bookmark.id());
  912. }
  913.  
  914. favoritesService.prototype.writeBookmarks =
  915. function (aBookmarksFile)
  916. {
  917.   var bsvc = Cc["@mozilla.org/browser/bookmarks-service;1"].getService(Ci.flockIBookmarksService);
  918.   var dirService = Cc[DIRECTORY_SVC_CONTRACTID].getService(Ci.nsIProperties);
  919.   var ds = RDFS.GetDataSource("rdf:flock-favorites").QueryInterface(Ci.nsIRDFDataSource);
  920.   var resource = RDF_FOLDERSROOT;
  921.   bsvc.WriteBookmarks(aBookmarksFile, ds, resource);
  922. }
  923.  
  924. favoritesService.prototype.importBookmarks =
  925. function favoritesService_importBookmarks(aBookmarksFile)
  926. {
  927.   // Make sure the file exists and is readable
  928.   aBookmarksFile.QueryInterface(Ci.nsIFile);
  929.   if ((!aBookmarksFile.exists()) || (!aBookmarksFile.isReadable())) {
  930.     throw Cr.NS_ERROR_UNEXPECTED;
  931.   }
  932.  
  933.   // Load the contents of the file into 'lines' array
  934.   var fis = Cc["@mozilla.org/network/file-input-stream;1"]
  935.     .createInstance(Ci.nsIFileInputStream);
  936.   fis.init(aBookmarksFile, 1, 0, 0);
  937.   fis.QueryInterface(Ci.nsILineInputStream);
  938.   var lines = []; var line = {};
  939.   while (fis.readLine(line)) { lines.push(line.value); }
  940.  
  941.   // Local functions to import bookmarks and folders
  942.   var inst = this;
  943.   var decodeEntities = function (str) {
  944.     return str.replace(/</g, "<")
  945.               .replace(/>/g, ">")
  946.               .replace(/&/g, "&")
  947.               .replace(/"/g, "\"")
  948.               .replace(/'/g, "'");
  949.   };
  950.   var importBookmark = function () {
  951.     var url, lastVisit, lastMod, id, name, desc, feedurl;
  952.     if (lines[i].match(/ HREF="([^"]*)"/)) { url = RegExp.$1; }
  953.     if (lines[i].match(/ FEEDURL="([^"]*)"/)) { feedurl = RegExp.$1; }
  954.     if (lines[i].match(/ LAST_VISIT="([^"]*)"/)) { lastVisit = RegExp.$1; }
  955.     if (lines[i].match(/ LAST_MODIFIED="([^"]*)"/)) { lastMod = RegExp.$1; }
  956.     if (lines[i].match(/ ID="([^"]*)"/)) { id = RegExp.$1; }
  957.     if (lines[i].match(/">(.*)<\/A>/)) { name = decodeEntities(RegExp.$1); }
  958.     if (lines[i+1].match(/^ *<DD>(.*)$/)) { desc = decodeEntities(RegExp.$1); i++; }
  959.     var bmRes;
  960.     if (feedurl) {
  961.       bmRes = inst.createLivemark(name, url, feedurl, desc);
  962.     }
  963.     else if (url == undefined) {
  964.       bmRes = inst.createSeparator();
  965.     }
  966.     else {
  967.       bmRes = inst.createBookmarkInternal(name, url, desc, new Date(lastVisit), new Date(lastMod));
  968.     }
  969.     return inst.faves_coop.get(bmRes.Value);
  970.   };
  971.   var importFolder = function () {
  972.     lines[i++].match(/">(.*)<\/H3/i);
  973.     var folderRes = inst.createFolder(RegExp.$1);
  974.     var c_folder = inst.faves_coop.get(folderRes.Value);
  975.     inst.faves_coop.bookmarks_root.children.remove(c_folder);
  976.     if (lines[i].match(/^ *<DD>(.*)$/i)) {
  977.       c_folder.description = decodeEntities(RegExp.$1);
  978.       i++;
  979.     }
  980.     if (lines[i].match(/^ *<DL><p>/i)) {
  981.       // This folder contains stuff
  982.       importContents(c_folder);
  983.     }
  984.     return c_folder;
  985.   };
  986.   var importContents = function (aParentFolder) {
  987.     i++; // Increment past <DL><p>
  988.     while (i < lines.length) {
  989. //      inst._logger.warn("\n Parent: "+aParentFolder.Value+" line: "+lines[i]);
  990.       var child = null;
  991.       if (lines[i].match(/^ *<DT><A /i)) {
  992.         child = importBookmark();
  993.       } else if (lines[i].match(/^ *<DT><H3/i)) {
  994.         child = importFolder();
  995.       } else if (lines[i].match(/^ *<\/DL><p>/i)) {
  996.         return;
  997.       }
  998.       if (child) {
  999.         aParentFolder.children.addOnce(child);
  1000.       }
  1001.       i++;
  1002.     }
  1003.   };
  1004.  
  1005.   // Begin parsing the file
  1006.   var i = 0;
  1007.   while ((!lines[i].match(/^ *<DL><p>/i)) && (i < lines.length)) { i++; }
  1008.   var folder = this.faves_coop.get(this.createFolder("Imported Bookmarks").Value);
  1009.   importContents(folder);
  1010. }
  1011. // END flockIFavoritesService
  1012.  
  1013.  
  1014.  
  1015. // BEGIN nsIBookmarksService
  1016. favoritesService.prototype.addBookmarkImmediately = function (aURI, aTitle, bmType, aDocCharSet) 
  1017.   return this.createBookmark(aName, aURI, aURI, "", aDocCharSet, null);
  1018. }
  1019.  
  1020. favoritesService.prototype.cloneResource = function (aSource) 
  1021. {
  1022.   var newResource = RDFS.GetAnonymousResource();
  1023.  
  1024.   // We need to create CoopType first or the indexer complains
  1025.   var coopType = this.dataSource.GetTarget(aSource, RDF_COOPTYPE, true);
  1026.   if (coopType)
  1027.     this.dataSource.Assert(newResource, RDF_COOPTYPE, coopType, true);
  1028.  
  1029.   var arcs = this.dataSource.ArcLabelsOut(aSource);
  1030.  
  1031.   while (arcs.hasMoreElements()) {
  1032.     var property = arcs.getNext();
  1033.     property = property.QueryInterface(Ci.nsIRDFResource);
  1034.     if (property.EqualsNode(this.getBookmarksToolbarFolder())) continue;
  1035.     if (property.EqualsNode(RDF_COOPTYPE)) continue; // Already created
  1036.     if (property.EqualsNode(FLRDF('isIndexable'))) continue; // Will be created later
  1037.     
  1038.     var target = this.dataSource.GetTarget(aSource, property, true);
  1039.     
  1040.     // Test if the arc points to a child.
  1041.     if (RDFCU.IsOrdinalProperty(property)) {
  1042.       var oldChild = target.QueryInterface(Ci.nsIRDFResource);
  1043.       var newChild = this.cloneResource(oldChild);
  1044.       this.dataSource.Assert(newResource, property, newChild, true);
  1045.     } else {
  1046.       this.dataSource.Assert(newResource, property, target, true);
  1047.     }
  1048.   }
  1049.   
  1050.   // Create isIndexable last to avoid firing the indexer too often
  1051.   var isIndexable = this.dataSource.GetTarget(aSource, FLRDF('isIndexable'), true);
  1052.   if (isIndexable)
  1053.     this.dataSource.Assert(newResource, FLRDF('isIndexable'), isIndexable, true);
  1054.   
  1055.   return newResource;
  1056. }
  1057.  
  1058. favoritesService.prototype.createBookmark =
  1059. function (aName, aURL, aShortcutURL, aDescription, aDocCharSet, aPostData)
  1060. {
  1061.   if (!aURL || aURL == null) aURL = "about:blank";
  1062.  
  1063.   var date = new Date();
  1064.   var bookmark = new this.faves_coop.Bookmark(
  1065.    {
  1066.       name: aName,
  1067.       URL: aURL,
  1068.       description: aDescription,
  1069.       BookmarkAddDate: date,
  1070.       LastVisitDate: date
  1071.     }
  1072.   );
  1073.   return RDFS.GetResource(bookmark.id());
  1074. }
  1075.  
  1076. favoritesService.prototype.createBookmarkInternal =
  1077. function (aName, aURL, aDescription, aLastVisit, aLastMod)
  1078. {
  1079.   if (!aURL || aURL == null) aURL = "about:blank";
  1080.  
  1081.   var bookmark = new this.faves_coop.Bookmark(
  1082.      {
  1083.       name: aName,
  1084.       URL: aURL,
  1085.       description: aDescription,
  1086.       LastVisitDate: aLastVisit,
  1087.       LastModifiedDate: aLastMod
  1088.     }
  1089.   );
  1090.   return RDFS.GetResource(bookmark.id());
  1091. }
  1092.  
  1093. favoritesService.prototype.createFolder = function (aName) 
  1094. {
  1095.   if (!aName || aName == null) aName = "New Folder";
  1096.   var folder = new this.faves_coop.Folder({
  1097.     BookmarkAddDate: new Date(),
  1098.     name: aName
  1099.   });
  1100.   this.faves_coop.bookmarks_root.children.addOnce(folder);
  1101.   return RDFS.GetResource(folder.id());
  1102. }
  1103.  
  1104. favoritesService.prototype.createFolderInContainer =
  1105. function (aName, aParentFolder, aIndex)
  1106. {
  1107.   var folder = new this.faves_coop.Folder({
  1108.     BookmarkAddDate: new Date(),
  1109.     name: aName
  1110.   });
  1111.  
  1112.   // default to the favorites_root folder...
  1113.   var parent = this.faves_coop.favorites_root;
  1114.   if (aParentFolder) {
  1115.     // FIXME: get should be able to take an nsIRDFResource as an id
  1116.     parent = this.faves_coop.get(aParentFolder.ValueUTF8)
  1117.   }
  1118.   if (aIndex < 0)
  1119.     parent.children.add(folder);
  1120.   else
  1121.     parent.children.insertAt(folder, aIndex);
  1122.  
  1123.   return RDFS.GetResource(folder.id());
  1124. }
  1125.  
  1126. favoritesService.prototype.createLivemark =
  1127. function (aName, aURL, aRSSURL, aDescription)
  1128. {
  1129.   var feedService = Cc["@flock.com/feed-manager;1"].getService(Ci.flockIFeedManager);
  1130.   var livemarkContext = feedService.getFeedContext("livemarks");
  1131.   var feed = livemarkContext.getRoot().subscribeURL(aRSSURL, aName);
  1132.   return RDFS.GetResource(feed.id());
  1133. }
  1134.  
  1135. favoritesService.prototype.createLivemarkInContainer =
  1136. function FS_createLivemarkInContainer(aName, aURL, aRSSURL, aDescription, aFolder, aIndex)
  1137. {
  1138.   var feedService = Cc["@flock.com/feed-manager;1"].getService(Ci.flockIFeedManager);
  1139.   var livemarkContext = feedService.getFeedContext("livemarks");
  1140.  
  1141.   parent_ = this.faves_coop.get(aFolder.ValueUTF8)
  1142.   if (!parent_) { // Invalid folder
  1143.     throw Components.Exception('createLivemarkInContainer: invalid folder '+aFolder.ValueUTF8,
  1144.                                 Cr.NS_ERROR_UNEXPECTED);
  1145.   }
  1146.  
  1147.   var feed = livemarkContext.getRoot().subscribeURL(aRSSURL, aName);
  1148.   var coopFeed = this.faves_coop.get(feed.id());
  1149.  
  1150.   if (aIndex < 0) {
  1151.     parent_.children.add(coopFeed);
  1152.   }
  1153.   else {
  1154.     parent_.children.insertAt(coopFeed, aIndex);
  1155.   }
  1156. }
  1157.  
  1158. favoritesService.prototype.createSeparator = function ()
  1159.   var newSeparator = new this.faves_coop.Separator();
  1160.   return RDFS.GetResource(newSeparator.id());
  1161. }
  1162.  
  1163. favoritesService.prototype.getBookmarksToolbarFolder = function ()
  1164. {
  1165.   if (this.faves_coop.toolbar.folder) {
  1166.     return RDFS.GetResource(this.faves_coop.toolbar.folder.id());
  1167.   } else {
  1168.     // JMC - Always have a toolbar folder
  1169.     var favesToolbar = new this.faves_coop.Folder({name: "Favorites Toolbar"});
  1170.     this.faves_coop.toolbar.folder = favesToolbar;
  1171.     this.faves_coop.bookmarks_root.children.addOnce(this.faves_coop.toolbar.folder);
  1172.     return this.getBookmarksToolbarFolder();
  1173.   }
  1174. }
  1175.  
  1176. favoritesService.prototype.getLastCharset = function (aURL) { return null; }
  1177.  
  1178. favoritesService.prototype.getParent = function (aSource) 
  1179.   if (!aSource || !aSource.QueryInterface || !aSource.QueryInterface(Ci.nsIRDFNode)) return false;
  1180.  
  1181.   var arcs = this.dataSource.ArcLabelsIn(aSource);
  1182.   while (arcs && arcs.hasMoreElements()) {
  1183.     var resource = arcs.getNext();
  1184.     if (resource && resource.QueryInterface) {
  1185.       resource = resource.QueryInterface(Ci.nsIRDFResource);
  1186.     }
  1187.     if (resource && resource.QueryInterface && RDFCU.IsOrdinalProperty(resource)) {
  1188.       var parent = this.dataSource.GetSource(resource, aSource, true);
  1189.       return parent;
  1190.     }
  1191.   }
  1192.   return null;
  1193. }
  1194.  
  1195. favoritesService.prototype.getParentChain = function (aSource) 
  1196.   var chain = [];
  1197.   var current = this.getParent(aSource);
  1198.   while (current) {
  1199.     this._logger.info("parentChain: "+current);
  1200.     chain.push(current);
  1201.     current = this.getParent(current);
  1202.   }
  1203.   chain.reverse();
  1204.   return makeNSIArray(chain); 
  1205. }
  1206.  
  1207. favoritesService.prototype.isBookmarked = function (aURL)
  1208. {
  1209.   return this.faves_coop.Bookmark.get_from_url(aURL) != null;
  1210. }
  1211.  
  1212. favoritesService.prototype.isBookmarkedResource = function (aResource)
  1213. {
  1214.   return this.faves_coop.Bookmark.exists(aResource.Value);
  1215. }
  1216.  
  1217. favoritesService.prototype.readBookmarks = function () { return; }
  1218.  
  1219. favoritesService.prototype.removeBookmarkIcon = function (aURL) { return; }
  1220.  
  1221. favoritesService.prototype.resolveKeyword = function (aName, aPostData) 
  1222. {
  1223.   var keywordArray = this.faves_coop.Bookmark.find({ShortcutURL: aName})
  1224.   var keyword = null;
  1225.   if (keywordArray && keywordArray.length == 1) {
  1226.     keyword = keywordArray[0].URL;
  1227.   }
  1228.   return keyword; 
  1229. }
  1230.  
  1231. favoritesService.prototype.updateBookmarkIcon =
  1232. function (aURL, aIconMIMEtype, aIconData, aIconDataLen) 
  1233. {
  1234.   var data = "data:";
  1235.   if (aIconData) {
  1236.     iconData = String.fromCharCode.apply(null, aIconData);
  1237.     data += aIconMIMEtype;
  1238.     data += ";base64,";
  1239.     data += base64Encode(iconData);
  1240.   }
  1241.  
  1242.   var faves = this.faves_coop.Bookmark.find({URL: aURL});
  1243.   for (var i = 0; i < faves.length; i++) {
  1244.     var fav = faves[i];
  1245.     fav.favicon = data;
  1246.   }
  1247.   return;
  1248. }
  1249.  
  1250. favoritesService.prototype.updateLastVisitedDate = function (aURL, docCharset) {
  1251.   if (aURL == '') return;
  1252.  
  1253.   var literal = RDFS.GetLiteral(aURL);
  1254.   var sources = this.dataSource.GetSources(RDF_URL, literal, true);
  1255.   var bookmark = null;
  1256.   while (sources && sources.hasMoreElements()) {
  1257.     var source = sources.getNext().QueryInterface(Ci.nsIRDFResource);
  1258.  
  1259.     var ctRes = this.dataSource.GetTarget(source, FLRDF("CoopType"), true);
  1260.     if (ctRes) {
  1261.       ctRes.QueryInterface(Ci.nsIRDFResource);
  1262.       if (ctRes.Value == "http://flock.com/rdf#Bookmark") {
  1263.         bookmark = this.faves_coop.get_from_resource(source);
  1264.         break;
  1265.       }
  1266.     }
  1267.   }
  1268.  
  1269.   if (bookmark) {
  1270.     bookmark.LastVisitDate = new Date();
  1271.   }
  1272.   return;
  1273. }
  1274.  
  1275.  
  1276. //check if string is an integer
  1277. favoritesService.prototype.isInt = function (str) {
  1278.     var i = parseInt (str);
  1279.  
  1280.     if (isNaN (i))
  1281.         return false;
  1282.  
  1283.     i = i . toString ();
  1284.     if (i != str)
  1285.         return false;
  1286.  
  1287.     return true;
  1288. }
  1289. // END nsIBookmarksService
  1290.  
  1291.  
  1292.  
  1293. // BEGIN flockIMigratable
  1294. favoritesService.prototype.needsMigration =
  1295. function favoritesService_needsMigration(oldVersion)
  1296. {
  1297.   var oldFavoritesFile = Cc["@mozilla.org/file/directory_service;1"] 
  1298.     .getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
  1299.   oldFavoritesFile.append(OLD_FAVORITES_RDF_FILE);
  1300.  
  1301.   var relicFavoritesFile = Cc["@mozilla.org/file/directory_service;1"] 
  1302.     .getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
  1303.   relicFavoritesFile.append(OLD_FAVORITES_RDF_FILE_RELIC);
  1304.  
  1305.   // only migrate if the old favorites rdf file exists
  1306.   // FIXME: after the flock_favorites.rdf file is renamed to flock_favorites_old.rdf
  1307.   // flock_favorites.rdf is created again in the same dir. so we need the 2nd
  1308.   // condition in the IF to stop migration from happening again
  1309.   if (oldFavoritesFile.exists() && !relicFavoritesFile.exists()) {
  1310.     return true;
  1311.   }
  1312.   return false;
  1313. }
  1314.  
  1315. favoritesService.prototype.startMigration = 
  1316. function (oldVersion, aFlockMigrationProgressListener)
  1317. {
  1318.   // Tell myworld to stop observing.
  1319.   var obs = getObserverService();
  1320.   obs.notifyObservers(null, 'myworld-enableDisable-observers', false);
  1321.   
  1322.   var oldFavoritesFile = Cc["@mozilla.org/file/directory_service;1"] 
  1323.     .getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
  1324.   oldFavoritesFile.append(OLD_FAVORITES_RDF_FILE);
  1325.  
  1326.   var ctxt = {
  1327.     listener: aFlockMigrationProgressListener,
  1328.     oldFavoritesFile: oldFavoritesFile
  1329.   };
  1330.  
  1331.   if (oldFavoritesFile.exists()) {
  1332.     ctxt.listener.onUpdate(0, 'Migrating favorites');
  1333.   }
  1334.   return { wrappedJSObject: ctxt };
  1335. }
  1336.  
  1337. favoritesService.prototype.doMigrationWork =
  1338. function (ctxtWrapper)
  1339. {
  1340.   var ctxt = ctxtWrapper.wrappedJSObject;
  1341.  
  1342.   if (!ctxt.oldFavoritesFile.exists())
  1343.     return false;
  1344.  
  1345.   if (!ctxt.favoritesMigrator)
  1346.     ctxt.favoritesMigrator = this._migrateFavorites(ctxt);
  1347.   if (ctxt.favoritesMigrator.next())
  1348.     ctxt.favoritesMigrator = null;
  1349.  
  1350.   return Boolean(ctxt.favoritesMigrator);
  1351. }
  1352.  
  1353. favoritesService.prototype.finishMigration = function (ctxtWrapper) { 
  1354.   // Re-init the topfaves
  1355.   for (var folderURN in gTopFaves) {
  1356.     var faveType = gTopFaves[folderURN];
  1357.  
  1358.     // start gTopFaves init function
  1359.     faveType.initialize(faveType,this,null);
  1360.   }  
  1361.   
  1362.   // Tell myworld to resume rdf observation
  1363.   var obs = getObserverService();
  1364.   obs.notifyObservers(null, 'myworld-enableDisable-observers', true);
  1365. }
  1366. // END flockIMigratable
  1367.  
  1368.  
  1369. favoritesService.prototype.deleteTransients =
  1370. function favoritesService_deleteTransients()
  1371. {
  1372.   this._logger.info(".deleteTransients()");
  1373.   var types = ["Account", "Notification", "OnlineBookmarksStream", "MediaQuery"];
  1374.   for (var t in types) {
  1375.     var type = types[t];
  1376.     this._logger.info(" deleting transients of type '"+type+"'");
  1377.     var transientArray = this.faves_coop[type].find({isTransient: true});
  1378.     for (var i = 0; i < transientArray.length; i++) {
  1379.       var transient_ = transientArray[i];
  1380.       this._logger.info('gonna remove transient named ' + transient_.name);
  1381.       var transientParents = transient_.getParents();
  1382.       for (var j = 0; j < transientParents.length; j++) {
  1383.         transientParents[j].children.remove(transient_);
  1384.       }
  1385.       if (transient_.children) {
  1386.         var childEnum = transient_.children.enumerate();
  1387.         while (childEnum.hasMoreElements()) {
  1388.           var child = childEnum.getNext();
  1389.           transient_.children.remove(child);
  1390.           if (!child.getParent()) child.destroy(); 
  1391.         }
  1392.       }
  1393.       transient_.destroy();
  1394.     }
  1395.   }
  1396. }  
  1397.  
  1398. favoritesService.prototype.Seq = function (node) {
  1399.   // make the resource a sequence if its not and return a container object
  1400.   if (!this.isSeq(node)) {
  1401.     return RDFCU.MakeSeq(this.dataSource, node);
  1402.   }
  1403.   var container = Cc[CONTAINER_CONTRACTID].createInstance(Ci.nsIRDFContainer);
  1404.   container.Init(this.dataSource, node);
  1405.   return container;
  1406. }
  1407.  
  1408. favoritesService.prototype._Seq = favoritesService.prototype.Seq;
  1409.  
  1410. favoritesService.prototype.isSeq = function (node) {
  1411.   // this._logger.info ("Called isSeq with node " + node + "\n");
  1412.   // is the node a sequence
  1413.   return RDFCU.IsSeq(this.dataSource, node);
  1414. }
  1415.  
  1416.  
  1417. favoritesService.prototype.getContainedActions = function (aURL)
  1418. {
  1419.   return simpleEnumerator(this._getContainedActions(aURL, null));
  1420. }
  1421.  
  1422. favoritesService.prototype._getContainedActions =
  1423. function (aURL, aArrIdentityActions)
  1424. {
  1425.   var getIdentityActions = function (aIdentity, identityActions) {
  1426.     var enabledActions = aIdentity.enabledAction.enumerate();
  1427.     while (enabledActions.hasMoreElements()) {
  1428.       var action = enabledActions.getNext();
  1429.       this._logger.info("adding action " + action.name + " to identityActions ");
  1430.       identityActions.push({action: action, actionName: action.name, identity: aIdentity});
  1431.     }
  1432.   };
  1433.   if (!aArrIdentityActions) {
  1434.     aArrIdentityActions = [];
  1435.   }
  1436.   var container = this.faves_coop.get(aURL);
  1437.   if (container instanceof this.faves_coop.Folder) {
  1438.     // Recurse for each contained Person
  1439.     var persons = container.children.enumerate();
  1440.     while (persons.hasMoreElements()) {
  1441.       this._getContainedActions(aChild.id(), aArrIdentityActions);
  1442.     }
  1443.   }
  1444.   if (container instanceof this.faves_coop.Person) {
  1445.     var identities = container.children.enumerate();
  1446.     while (identities.hasMoreElements()) {
  1447.       var identity = identities.getNext();
  1448.       getIdentityActions(identity, aArrIdentityActions);
  1449.     }
  1450.   }
  1451.   // return an enumerator of actions?
  1452.   return aArrIdentityActions;
  1453. }
  1454.  
  1455. favoritesService.prototype.createSeparatorInContainer =
  1456. function (aParentFolder, aIndex)
  1457. {
  1458.   var parentFolder = this.faves_coop.get_from_resource(aParentFolder);
  1459.   if (parentFolder instanceof this.faves_coop.Stream) throw NS_RDF_ASSERTION_REJECTED;
  1460.   var newSeparator = new this.faves_coop.Separator();
  1461.   parentFolder.children.insertAt(newSeparator, aIndex);
  1462.   return RDFS.GetResource(newSeparator.id());
  1463. }
  1464.  
  1465.  
  1466. favoritesService.prototype.createNewBookmarkWithTags =
  1467. function (aName, aURL, aShortcutURL, aDescription, aDocCharSet, aPostData, aTags)
  1468. {
  1469.   var now = new Date();
  1470.   
  1471.   if (!aURL || aURL == null) aURL = "about:blank";
  1472.  
  1473.   var shared = false;
  1474.   if ( PREFS.getPrefType("flock.bookmarks.shouldnotshare") &&
  1475.        PREFS.getBoolPref("flock.bookmarks.shouldnotshare") == false )
  1476.   {
  1477.     shared = true;
  1478.   }
  1479.   return new this.faves_coop.Bookmark(
  1480.       {
  1481.       name: aName,
  1482.       URL: aURL,
  1483.       description: aDescription,
  1484.       tags: aTags,
  1485.       shared: shared,
  1486.       ShortcutURL: aShortcutURL,
  1487.       BookmarkAddDate: now,
  1488.       LastModifiedDate: now,
  1489.       LastVisitDate: now,
  1490.       LastCharset: aDocCharSet
  1491.     }
  1492.   );
  1493. }
  1494.  
  1495. favoritesService.prototype.setBookmarksToolbarFolder = function (aSource)
  1496. {
  1497.   var folder = this.faves_coop.get_from_resource(aSource);
  1498.   if (folder) {
  1499.     this.faves_coop.toolbar.folder = folder;
  1500.   }
  1501. }
  1502.  
  1503. favoritesService.prototype.removeFolder = function (aFolder)
  1504. {
  1505.   // verify the folderness
  1506.   if (!this.isFolderResource(aFolder)) {
  1507.     return false;
  1508.   }
  1509.   return true;
  1510.   // FIXME: remove all the children
  1511.   // FIXME: gc the children as they're removed
  1512.   // FIXME: remove the name and the type
  1513.   // FIXME: remove the Seq-ness
  1514.   // FIXME: remove the folder from it's parent
  1515.   // FIXME: return success
  1516. }
  1517.  
  1518. favoritesService.prototype.renameFolder =
  1519. function (aFolder, aNewName)
  1520. {
  1521.   // verify the folderness
  1522.   if (!this.isFolderResource(aFolder)) {
  1523.     return false;
  1524.   }
  1525.   return true;
  1526.   // FIXME: get the old name...
  1527.   // FIXME: ds.Change RDF_NAME
  1528.   // FIXME: return success
  1529. }
  1530.  
  1531. favoritesService.prototype.moveFolder =
  1532. function (aFolder, aNewParent)
  1533. {
  1534.   // verify the folderness
  1535.   if (!this.isFolderResource(aFolder)) {
  1536.       return false;
  1537.   }
  1538.   var folder = this.faves_coop.get(aFolder.ValueUTF8);
  1539.   var newparent = this.faves_coop.favorites_root;
  1540.   if (aNewParent != null) {
  1541.     newparent = this.faves_coop.get(aNewParent.ValueUTF8);
  1542.   }
  1543.   return true;
  1544.   // FIXME: get old parent folder
  1545.   // FIXME: remove from old parent folder
  1546.   // FIXME: add to new parent folder
  1547.   // FIXME: return success
  1548. }
  1549.  
  1550. favoritesService.prototype.getFolderName = function (aFolder)
  1551. {
  1552.   // verify the folderness
  1553.   if (!this.isFolderResource(aFolder)) {
  1554.     return null;
  1555.   }
  1556.   var folder = this.faves_coop.get(aFolder.ValueUTF8);
  1557.   return folder.name;
  1558. }
  1559.  
  1560. favoritesService.prototype.createBookmarkInContainer =
  1561. function (aName, aURL, aShortcutURL, aDescription, aDocCharSet, aPostData, aFolder, aIndex)
  1562. {
  1563.   var bookmarkResource = this.createBookmark(aName, aURL, aShortcutURL, aDescription, aDocCharSet, aPostData);
  1564.   var bookmark = this.faves_coop.get(bookmarkResource.Value);
  1565.   var folder = this.faves_coop.get(aFolder.ValueUTF8);
  1566.  
  1567.   // FIXME: use index?
  1568.   folder.children.add(bookmark);
  1569.  
  1570.   //??? this.removeFavoriteFromFolder(aURL, this.favoritesRoot);
  1571. }
  1572.  
  1573. favoritesService.prototype.__defineGetter__('shortname', function () {
  1574.   return "Bookmarks";
  1575. });
  1576.  
  1577. favoritesService.prototype._migrateFavorites = function (ctxt)
  1578. {
  1579.   // a coop version of the bookmarks_root folder
  1580.   var realBookmarksRootFolder = this.faves_coop.bookmarks_root.children;
  1581.   // read into in-memory rdf
  1582.   var ds = RDFS.GetDataSourceBlocking(IOS.newFileURI(ctxt.oldFavoritesFile).spec);
  1583.   // coopify the rdf graph
  1584.   var resourceCount = 0;
  1585.   var resources = ds.GetAllResources();
  1586.  
  1587.   // helper function
  1588.   function _addBookmarkToFolderHelper(aScope, aFolder, aFav) {
  1589.     aScope.addBookmarkToFolder(aFolder, aFav.Name, aFav.URL, null,
  1590.                                aFav.description, null, null, aFav.tags);
  1591.   }
  1592.  
  1593.   while (resources.hasMoreElements()) {
  1594.     var resource = resources.getNext();
  1595.     var type_target = ds.GetTarget(resource, RDF_TYPE, true);
  1596.     if (type_target) {
  1597.       ds.Assert(resource, RDF_COOPTYPE, type_target, true);
  1598.       resourceCount++;
  1599.     }
  1600.   }
  1601.   var Coop = loadSubScript("chrome://browser/content/flock/common/coop.js").Coop;
  1602.   var migration_coop = new Coop(ds, "chrome://browser/content/flock/common/migration-coop.js");
  1603.   var folderRes;
  1604.   var favEnum = migration_coop.CardinalFavorite.all();
  1605.   var favesCount = 0;
  1606.   var folderID = this.faves_coop.bookmarks_root.id();
  1607.   while (favEnum.hasMoreElements()) {
  1608.     var fave = favEnum.getNext();
  1609.     if (!fave.Name) continue;
  1610.     if (fave.URL == "http://flock.com/rdf#FavoritesRoot") continue;
  1611.     var favParents = fave.getParents();
  1612.     if (favParents) {
  1613.       // fave.getParents() possibly has an array of null parents. These are
  1614.       // the bookmarks that are not put into a collection or bookmarks toolbar
  1615.       // in Cardinal. If there is only 1 entry in array, we need to add
  1616.       // that bookmark to the bookmarks root.
  1617.       if (favParents.length == 1 && !favParents[0]) {
  1618.         _addBookmarkToFolderHelper(this, folderID, fave);
  1619.       }
  1620.     }
  1621.     favesCount++;
  1622.     var percentage = Math.round(100 * (favesCount / resourceCount));
  1623.     ctxt.listener.onUpdate(percentage, "Migrating favorites");
  1624.     yield false;
  1625.   }
  1626.  
  1627.   // migrate collections
  1628.   var collEnum = migration_coop.CardinalCollection.all();
  1629.   var folderRes = RDFS.GetResource(this.faves_coop.bookmarks_root.id());
  1630.   while (collEnum.hasMoreElements()) {
  1631.     var coll = collEnum.getNext();
  1632.     var folderIndex = -1;
  1633.     if (!coll) {
  1634.       // We have to do this since anything that doesn't match the
  1635.       // migrations-coop schema is returned as null.
  1636.       continue;
  1637.     }
  1638.     if (!coll.Name) {
  1639.       continue;
  1640.     }
  1641.     if (coll.toolbar) {
  1642.       folderIndex = 1;
  1643.     }
  1644.     var folder = this.createFolderInContainer(coll.Name, folderRes, folderIndex);
  1645.     var collContents = coll.children.enumerate();
  1646.     while (collContents.hasMoreElements()) {
  1647.       fave = collContents.getNext();
  1648.       if (!fave) {
  1649.         // We have to do this since anything that doesn't match the
  1650.         // migrations-coop schema is returned as null.
  1651.         continue;
  1652.       }
  1653.       // Add bookmark into new folder from collection in Cardinal.
  1654.       _addBookmarkToFolderHelper(this, folder.ValueUTF8, fave);
  1655.     }
  1656.     if (coll.toolbar) {
  1657.       // Get the new folder's coop representation.
  1658.       var newFolder = this.faves_coop.get(folder.ValueUTF8);
  1659.       
  1660.       // get the bm toolbar folder name from Cormorant
  1661.       var bmToolbarId = this.getBookmarksToolbarFolder().Value;
  1662.       var bmToolbarName = this.faves_coop.get(bmToolbarId).name;
  1663.  
  1664.       // delete the default bookmark toolbar folder
  1665.       this.faves_coop.get(bmToolbarId).destroy();
  1666.  
  1667.       this.faves_coop.toolbar.folder = newFolder;
  1668.       
  1669.       // Change the toolbar folder name to Cormorant's folder name.
  1670.       this.faves_coop.toolbar.folder.name = bmToolbarName;
  1671.     }
  1672.   }
  1673.  
  1674.   // rename the file and delete the old copy
  1675.   RDFS.UnregisterDataSource(ds);
  1676.   ds = null;
  1677.   migration_coop.datasource = null;
  1678.   migration_coop = null;
  1679.   
  1680.   ctxt.oldFavoritesFile.moveTo(null, OLD_FAVORITES_RDF_FILE_RELIC);
  1681.   yield true;
  1682. }
  1683.  
  1684.  
  1685. favoritesService.prototype.notifyResolvedCharset =
  1686. function FS_notifyResolvedCharset (aCharset, aClosure) {
  1687.   throw Components.Exception("Unexpected call to NotifyResolvedCharset -- we never set aWantCharset to true!",
  1688.                              Cr.NS_ERROR_UNEXPECTED);
  1689. }
  1690.  
  1691. favoritesService.prototype.requestCharset =
  1692. function FS_requestCharset (aWebNavigation, aChannel, aWantCharset, aClosure) {
  1693.   aWantCharset = false;
  1694.   aClosure = null;
  1695.   
  1696.   var url = aChannel.URI.spec;
  1697.  
  1698.   var fav = this.faves_coop.get(url);
  1699.   if (!fav)
  1700.     return null;
  1701.   
  1702.   if (fav.LastCharset && fav.LastCharset != "")
  1703.     return fav.LastCharset;
  1704. }
  1705.  
  1706.  
  1707. // ================================================
  1708. // ========== BEGIN XPCOM Module support ==========
  1709. // ================================================
  1710.  
  1711. function createModule(aParams) {
  1712.   return {
  1713.     registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
  1714.       var aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  1715.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  1716.                                         aParams.contractID, aFileSpec,
  1717.                                         aLocation, aType );
  1718.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  1719.                                         "@mozilla.org/embeddor.implemented/bookmark-charset-resolver;1", aFileSpec,
  1720.                                         aLocation, aType );
  1721.       var catMgr = Cc["@mozilla.org/categorymanager;1"]
  1722.         .getService(Ci.nsICategoryManager);
  1723.       if (!aParams.categories) { aParams.categories = []; }
  1724.       for (var i = 0; i < aParams.categories.length; i++) {
  1725.         var cat = aParams.categories[i];
  1726.         catMgr.addCategoryEntry( cat.category, cat.entry,
  1727.                                  cat.value, true, true );
  1728.       }
  1729.     },
  1730.     getClassObject: function (aCompMgr, aCID, aIID) {
  1731.       if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
  1732.       if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
  1733.       return { // Factory
  1734.         createInstance: function (aOuter, aIID) {
  1735.           if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
  1736.           var comp = new aParams.componentClass();
  1737.           if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
  1738.           return comp.QueryInterface(aIID);
  1739.         }
  1740.       };
  1741.     },
  1742.     canUnload: function (aCompMgr) { return true; }
  1743.   };
  1744. }
  1745.  
  1746. // NS Module entrypoint
  1747. function NSGetModule(aCompMgr, aFileSpec) {
  1748.   return createModule({
  1749.     componentClass: favoritesService,
  1750.     CID: FAVORITES_CID,
  1751.     contractID: FAVORITES_CONTRACTID,
  1752.     componentName: "Favorites JS Component",
  1753.     categories: [
  1754.       { category: "flockMigratable", entry: "favorites", value: FAVORITES_CONTRACTID }
  1755.     ]
  1756.   });
  1757.  
  1758. }
  1759.  
  1760. // ========== END XPCOM Module support ==========
  1761.